package com.qf.smartplatform.log;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.qf.smartplatform.exceptions.MyBaseException;
import com.qf.smartplatform.log.annotations.LogAnnotation;
import com.qf.smartplatform.pojo.BaseUser;
import com.qf.smartplatform.pojo.SysOperLog;
import com.qf.smartplatform.pojo.SysUserInfo;
import com.qf.smartplatform.service.OperLogService;
import com.qf.smartplatform.utils.RequestUtil;
import com.qf.smartplatform.utils.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/*** @author Tgb
 * @version V1.0
 * @Project smartplatform
 * @Package com.qf.smartplatform.log
 * @Description:
 * @Date 2022/9/16 15:30
 */
@Component
@Aspect
public class LogAdvice {
    private OperLogService operLogService;
    @Autowired
    public void setOperLogService(OperLogService operLogService) {
        this.operLogService = operLogService;
    }

    private ObjectMapper objectMapper;
    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Pointcut("@annotation(com.qf.smartplatform.log.annotations.LogAnnotation)")
    public void Pointcut(){}

    @AfterReturning(pointcut = "Pointcut()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        SysOperLog sysOperLog = new SysOperLog();
        try {
            sysOperLog.setJsonResult(objectMapper.writeValueAsString(result));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        sysOperLog.setStatus(1L);
        sysOperLog.setErrorMsg("200  OK");
        addOperLog(joinPoint,sysOperLog);
    }

    @AfterThrowing(pointcut = "Pointcut()",throwing = "exp")
    public void afterThrowing(JoinPoint joinPoint,Exception exp){
        SysOperLog sysOperLog = new SysOperLog();
        sysOperLog.setStatus(1L);
    if (exp instanceof MyBaseException){
        sysOperLog.setErrorMsg(((MyBaseException) exp).getCode()+","+exp.getMessage());
    }else{
        sysOperLog.setErrorMsg(exp.getMessage());
    }
        addOperLog(joinPoint,sysOperLog);
    }

    public void addOperLog(JoinPoint joinPoint, SysOperLog operLog){


        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //获得当前方法名称
        String methodName = method.getName();



        //根据当前方法获取方法的请求参数,将其放到map中转为json串后存入数据库中
        HashMap<Object, Object> hashMap = new HashMap<>();
        Parameter[] parameters = method.getParameters();
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < parameters.length; i++) {
            hashMap.put(parameters[i].getName(),args[i]);
        }
        try {
            String parameter = objectMapper.writeValueAsString(hashMap);
            operLog.setOperParam(parameter);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }

        //获得当前拦截的注解的参数---> title 和 business_type 的值
        LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
        String title = annotation.title();
        Long business_type = Long.valueOf(annotation.business_type());

        //请求相关的数据可以从request中获取,比如地址, 请求方式,ip,访问的客户端类型,
        //传递的参数其实就是方法的参数,我们可以将方法的参数获取到,然后转成数据保存过去,但是我们的参数可能会有多个,但是在表中我们只有一个列,怎么用一个列来保存所有的值?
        //我们可以将这些参数转成json,放到一个map中转成json
        //获取数据,保存到数据库
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //获取请求的方式
        String requestMethod = request.getMethod();
        //用RequestUtil工具包获取，根据request 获取 ip地址
        String ip = RequestUtil.getRemoteHost(request);
        //根据ip地址获取ip所在的操作地点
        String location = RequestUtil.getLocationByIp(ip);
        //获取操作人员信息
        BaseUser user = SecurityUtils.getUser();
        String username = user.getUsername();
        //获取 请的URL
        String requestURI = request.getRequestURI();

        Map<String, String> osAndBrowserInfo = RequestUtil.getOsAndBrowserInfo(request);
        String os = osAndBrowserInfo.get("os");
        switch (os) {
            case "Android":
            case "Iphone":
                operLog.setOperatorType(2L);
                break;
            case "Windows":
            case "Mac":
            case"Unix":
                operLog.setOperatorType(1L);
                break;
            default:
                operLog.setOperatorType(0L);
                break;
        }

        operLog.setTitle(title);
        operLog.setBusinessType(business_type);
        operLog.setMethod(methodName);
        operLog.setRequestMethod(requestMethod);
        operLog.setOperName(username);
        operLog.setOperUrl(requestURI);
        operLog.setOperIp(ip);
        operLog.setOperLocation(location);
        operLog.setOperTime(new Date());
        operLogService.addOperLog(operLog);


    }
}
